<?php


include_once"objectsHandler.php";
include_once "dataHandlerClass.php";
include_once "elementHandlerClass.php";
include_once "structHandlerClass.php";
/**
 * This class handles the XML structure, and allows accessing and saving the information of the structure.
 */
class XMLHandler {

    //Declaration of objects of our project.
    var $XMLdata;
    var $arrayElement ;
    var $arrayData;
    var $arrayStruct;

    private $separadorRelacion;
    private $separadorIds;
    private $separadorArbol;
    private $nombreRaiz;

    /**
     *Main constructor of the class.
     * @param <array> $parametros It contains information of the set of characters to use.
     */
    public function  __construct($parametros) {

        $this->separadorRelacion=$parametros['id_char'];
        $this->separadorIds=$parametros['separator'];
        $this->nombreRaiz=$parametros['root_name'];

        $arrayElement = array();
        $arrayData= array();
        $arrayStruct= array();
    }

    function getArrayElement() {
        return $this->arrayElement;
    }

    function getArrayData() {
        return $this->arrayData;
    }

    function getArrayStruct() {
        return $this->arrayStruct;
    }

    /**
     * This function check if a XML is valid refered to its DTD.
     * @global <XML> $XMLdata XML to check.
     * @return <boolean> True if XML is validate, false otherwise.
     */
    public function isValid() {

        global $XMLdata;

        if ($XMLdata->validate()) {
            return true;
        }else return false;
    }

    /**
     *This function will load all the nodes contained inside the XML structure.
     * @global <xml> $XMLdata Contains all the information of the XML
     * @param <string> $path Path where the XML is located.
     * @return <boolean> True if the XML has been correctly loaded. False otherwise.
     */
    public function loadXML($path) {

        global $XMLdata;

        $XMLdata = new DOMDocument();
        //$XMLdata->load( 'nuevo.xml' );

        if (file_exists($path)) {
            $XMLdata->load( $path );
            //print_r($XMLdata);
            return true;
        }
        else {
            echo "LOADXML error, couldn't find the file.";
            return false;
        }
    }

    /**
     * Sort an array using a function to compare elements.
     */
    function sortElements() {
        usort($this->arrayElement, array("XMLHandler", "cmpNumElements"));
    }

    /**
     *Function that is used to compare two elements of an array of elements.
     * @param <element> $a First element to compare.
     * @param <element> $b Second element to compare
     * @return <integer> 1 if the element are already placed in order.
     */
    function cmpTypes($a, $b) {
        return strcmp ($a->getType() , $b->getType()) ;
    }
    /**
     *Function that is used to compare two elements of an array of elements.
     * @param <element> $a First element to compare.
     * @param <element> $b Second element to compare
     * @return <integer> 1 if the element are already placed in order.
     */
    function cmpNumElements($a, $b) {
        return strcmp ($a->getNumElements() , $b->getNumElements()) ;
    }
    /**
     *Function that is used to compare two elements of an array of elements.
     * @param <element> $a First element to compare.
     * @param <element> $b Second element to compare
     * @return <integer> 1 if the element are already placed in order.
     */
    function cmpValues($a, $b) {
        return strcmp ($a->getValue() , $b->getValue()) ;
    }

    /**
     *Check if a string begins with another string given as a parameter.
     * @param <string> $string String to check
     * @param <string> $search String to be found it
     * @return <boolean> It returns true if the string is found it, false otherwise.
     */
    function string_begins_with($string, $search) {
        return (strncmp($string, $search, strlen($search)) == 0);
    }

    /**
     * Auxiliar function to print the information of the structure array.
     */
    function printArraysStruct() {
        echo "<br><br>";
        foreach ($this->arrayStruct  as $element) {
            echo "VALUE: ".$element->getValue() . " tipo: ". $element->getType()." padre: ". $element->getParentName()." hermano: ". $element->getBrotherName()."<br>";
        }
    }

    /**
     * Auxiliar function to print the information of the element array.
     */
    function printArraysElement() {

        foreach ($this->arrayElement  as $element) {
            echo "Value: ".$element->getValue() . " tipo: ". $element->getType()."<br>";
        }
    }

    /**
     * Auxiliar function to print the information of the data array.
     */
    function printArraysData() {

        foreach ($this->arrayData  as $element) {
            echo "Path_Structure: ".$element->getPathStructure() . " y valor ". $element->getValue()." y identificador ". $element->getElementID()."<br>";
        }
    }

    /**
     *Function that generate all the nodes contained on the XML file.
     * @global <xml> $XMLdata Object that contains the xml information.
     */
    function generateNodes() {
        global $XMLdata;
        $nodo  = $XMLdata->getElementsByTagName( 'SISTEMA');
        //Extracting the root of the document;
        $nodo = $nodo->item(0);
        $nodo  = $XMLdata->getElementsByTagName( 'NODE');
        $nodo = $nodo->item(0);
        $nodoSimpleXML = simplexml_import_dom($nodo);
        $this->generateNodesRec("",$nodoSimpleXML,"",false,0);
        $this->sortElements();

    }

    /**
     *This function checks if exists an element in the structure
     * @param <type> $element String with the name of the element.
     * @return <type> True if exists, false otherwise.
     */
    function existsElementStructure($element) {
        if (count($this->arrayStruct)==0) return false;
        foreach ($this->arrayStruct  as $elementArray) {
            if    (!strcmp($element->getValue(),$elementArray->getValue() )&&
                    !strcmp($element->getType(),$elementArray->getType()) &&
                    !strcmp ($element->getParentName(),$elementArray->getParentName() ) &&
                    !strcmp ($element->getBrotherName() ,$elementArray->getBrotherName() )) {
                return true;
            }
        }
        return false;
    }

    /**
     *This function returns the metadata of a node in the xml schema.
     * @param <string> $node String with the name of the node.
     * @param <string> $attr String with the name of the attribute.
     * @return <string> string with the metadata.
     */
    function getMetadata($node,$attr) {
        $nodoSimpleXML = simplexml_import_dom($node);
        return $nodoSimpleXML->METADATA->$attr;

    }
    /**
     *This function checks if a node has children.
     * @param <string> $node String with the name of the node.
     * @return <bool> True if has children, false otherwise.
     */
    function hasChildren($node) {
        if ($node->CHILDREN=="") return false ; else return true;
    }

    /**
     *Auxiliar function to remove the first slash of a string.
     * @param <string> $string String to handle
     * @return <string> String with the first slash removed
     */
    function removeFirstSlash($string) {
        return $newarray = substr($string,1);
    }


    /**
     *This function implements the node generation from a XML using recursion
     * @param <string> $parentPath Path of the parent node.
     * @param <node> $child Descendant node of the current node.
     * @return <type>
     */
    function generateNodesREC($parentPath,$child, $lastIDelement,$lastItemWasElement,$numRelationElements) {


        //If the node is an element
        if ($this->string_begins_with($this->getMetadata($child,"TYPE"),"ELEMENT")) {
            //The node is added to the element array.
            if ($lastItemWasElement)
                $element = new ObjectElement($lastIDelement.$this->separadorIds.$this->getMetadata($child,"NAME").$this->separadorRelacion.$this->getMetadata($child,"ID"),$this->removeFirstSlash($parentPath.$this->separadorRelacion.$this->getMetadata($child,"NAME")),$numRelationElements+1);
            else $element = new ObjectElement($this->getMetadata($child,"NAME").$this->separadorRelacion.$this->getMetadata($child,"ID"),$this->removeFirstSlash($parentPath.$this->separadorIds.$this->getMetadata($child,"NAME")),1);
            $this->arrayElement[]= $element;
            //The node is added to the structure array.
            if ($lastItemWasElement)
                $node = new ObjectStruct($this->removeFirstSlash($parentPath).$this->separadorRelacion.$this->getMetadata($child,"NAME"),$this->getMetadata($child,"TYPE"),"","");
            else $node = new ObjectStruct($this->getMetadata($child,"NAME"),$this->getMetadata($child,"TYPE"),"","");
            if (!$this->existsElementStructure($node))
                $this->arrayStruct[]= $node;
            if ($this->hasChildren($child)) {
                foreach($child->CHILDREN->children() as $children) {
                    //The function is called recursively for all the nodes.
                    if ($lastItemWasElement)  $this->generateNodesREC($parentPath.$this->separadorRelacion.$this->getMetadata($child,"NAME"),$children,$lastIDelement.$this->separadorIds.$this->getMetadata($child,"NAME").$this->separadorRelacion.$this->getMetadata($child,"ID"),true,$numRelationElements+1);
                    else $this->generateNodesREC($this->separadorIds.$this->getMetadata($child,"NAME"),$children,$this->getMetadata($child,"NAME").$this->separadorRelacion.$this->getMetadata($child,"ID"),true,1);
                }return

                ;
            }
        }

        //If the node is an internal node...
        else   if ($this->getMetadata($child,"TYPE")=="NODE") {
            //The node is added to the structure array.
            if ($this->getMetadata($child,"NAME")=="SISTEMA") {
                foreach($child->CHILDREN->children() as $children) {

                    $this->generateNodesREC("",$children,"",false,$numRelationElements);
                }
            }
            else {
                $node = new ObjectStruct($this->getMetadata($child,"NAME"),$this->getMetadata($child,"TYPE"),$parentPath,"");
                if (!$this->existsElementStructure($node))
                    $this->arrayStruct[]= $node;

                foreach($child->CHILDREN->children() as $children) {
                    //The function is called recursively for all the nodes.
                    $this->generateNodesREC($parentPath.$this->separadorArbol.$this->getMetadata($child,"NAME"),$children,$lastIDelement,false,$numRelationElements);
                }return
                ;
            }
        }
        //Otherwise the node is a leaf (data of an element)
        else {
            //The node is added to the structure.
            $node = new ObjectStruct($this->getMetadata($child,"NAME"),$this->getMetadata($child,"TYPE"),$parentPath,"");
            if (!$this->existsElementStructure($node))
                $this->arrayStruct[]= $node;
            //The node is added to the data structure.
            $data = new ObjectData($parentPath.$this->separadorIds.$this->getMetadata($child,"NAME"), $this->getMetadata($child,"VALUE"),$lastIDelement);
            $this->arrayData[]= $data;
        }
    }
}
?>